package com.ecr.hub.service.impression.pdf.impl;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.annotation.Resource;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import com.ecr.hub.ApplicationException;
import com.ecr.hub.PlanNotFoundException;
import com.ecr.hub.RailNotFoundException;
import com.ecr.hub.SillonTypeException;
import com.ecr.hub.WagonNotFoundException;
import com.ecr.hub.model.composition.Composition;
import com.ecr.hub.model.enumeration.HubObjectActive;
import com.ecr.hub.model.enumeration.HubSillonStatus;
import com.ecr.hub.model.enumeration.HubSillonType;
import com.ecr.hub.model.rail.Rail;
import com.ecr.hub.model.sillon.Sillon;
import com.ecr.hub.model.user.User;
import com.ecr.hub.model.wagon.Wagon;
import com.ecr.hub.service.hub.IHubManager;
import com.ecr.hub.service.impression.pdf.IHubPdfCreator;
import com.ecr.hub.service.impression.pdf.IPdfManager;
import com.ecr.hub.service.rail.IRailManager;
import com.ecr.hub.util.ManagedBeanUtils;
import com.lowagie.text.Document;
import com.lowagie.text.DocumentException;
import com.lowagie.text.PageSize;
import com.lowagie.text.Rectangle;
import com.lowagie.text.pdf.PdfWriter;

@Service("pdfManager")
public class PdfManagerImpl implements IPdfManager {

	// Le séparateur htm.
	private static final String PATH_HTML_SEPARATOR = "/";

	// Les noms des différents documents.
	public static String PDF_RAIL = "photo_voie.pdf";
	public static String PDF_HUB = "photo_hub.pdf";
	public static String PDF_PLANNING = "planning_hub.pdf";
	public static String PDF_WORKING_PLAN = "programme_travail.pdf";
	public static String PDF_FORMATION_PLAN = "programme_formation.pdf";
	public static String PDF_COMPOSITION_PLAN = "releve_composition.pdf";

	// Propriété pour le répertoire de stockage des pdfs.
	@Value(value = "${pdf.directory.path}")
	private String pdfDirectoryPath;

	// Propriété pour le chemin de visualisation des pdfs.
	@Value(value = "${pdf.html.path}")
	private String pdfHtmlPath;

	// Propriété pour le logo du document pdf.
	@Value(value = "${pdf.img.path}")
	private String pdfImgFullPath;

	// Les ressources pour la création des pdf.
	@Resource(name = "hubPdfCreator")
	IHubPdfCreator hubPdfCreator;

	// Les ressources pour accéder aux bases.
	@Resource(name = "hubManager")
	IHubManager hubManager;

	@Resource(name = "railManager")
	IRailManager railManager;

	/**
	 * Cette méthode permet de regrouper l'ensemble des opérations nécessaires
	 * pour obtenir la création d'un pdfWriter à partir du chemin absolu d'un
	 * fichier. Si le répertoire pdf n'est pas trouvé, on le crée, si le
	 * répertoire utilisateur n'est pas trouvé (identifiant de l'utilisateur),
	 * alors on le crée. Si il y a un problème de création ou de lecture, dans
	 * tous les cas, la méthode va partir en exception au niveau de la création
	 * du pdfWriter.
	 */
	private PdfWriter getPdfWriter(String pdfFileName, User user,
			Document document) throws ApplicationException {

		try {
			File pdfFile = new File(
					ManagedBeanUtils.getRealPath(pdfDirectoryPath));

			if (!pdfFile.exists())
				pdfFile.mkdir();

			pdfFile = new File(pdfFile.getAbsolutePath() + File.separator
					+ user.getId());

			if (!pdfFile.exists())
				pdfFile.mkdir();

			pdfFile = new File(pdfFile.getAbsolutePath() + File.separator
					+ pdfFileName);
			pdfFile.createNewFile();

			PdfWriter pdfWriter = PdfWriter.getInstance(document,
					new FileOutputStream(pdfFile));

			return pdfWriter;
		} catch (IOException e) {
			throw new ApplicationException(e);
		} catch (DocumentException e) {
			throw new ApplicationException(e);
		}
	}

	/**
	 * Création d'un flux pdf pour la photo de tout ou partie des voies à partir
	 * du menu impression. On ouvre un pdf writer sur un fichier dédié à
	 * l'utilisateur. Comme cette méthode n'est pas prévue pour être souvent
	 * utilisée, les requêtes ne sont pas optimisées. On ne va pas rechercher le
	 * trafficController et on préfère toujours aller prendre directement en
	 * base de données car il faut aussi prendre les voies non actives. Très
	 * brutal et non optimisé..... jusqu'a 113 requêtes...
	 */
	@Override
	public String createPdfHub(User user, int typeBundleId)
			throws ApplicationException {

		List<Rail> lstRails;
		if (typeBundleId == 0) {
			lstRails = railManager.getAllList();
		} else {
			lstRails = railManager.getListByTypeBundle(typeBundleId,
					HubObjectActive.ALL.getValue());
		}

		for (Rail rail : lstRails) {
			try {
				List<Wagon> lstWagons = hubManager.getListWagonsForRail(rail);
				for (Wagon wagon : lstWagons) {
					rail.getLstWagons().add(wagon);
				}
			} catch (WagonNotFoundException e) {
				// RAS.
			}
		}

		// Suppression des voies sur lesquelles il n'y a aucun wagon.
		Iterator<Rail> it = lstRails.iterator();
		while (it.hasNext()) {
			if (it.next().getLstWagons().size() == 0)
				it.remove();
		}

		if (lstRails.size() == 0)
			throw new RailNotFoundException(
					"Hub : Il ne reste plus de voie contenant "
							+ "des wagons sur le hub.");

		Rectangle a4 = PageSize.A4;
		Rectangle a4Landscape = a4.rotate();
		Document document = new Document(a4Landscape);

		try {
			hubPdfCreator.pdfDocumentHub(getPdfWriter(PDF_HUB, user, document),
					document, ManagedBeanUtils.getRealPath(pdfImgFullPath),
					lstRails);
		} catch (DocumentException e) {
			throw new ApplicationException(e);
		}

		return pdfHtmlPath + PATH_HTML_SEPARATOR + user.getId()
				+ PATH_HTML_SEPARATOR + PDF_HUB;
	}

	/**
	 * Création d'un flux pdf pour la photo d'une voie à partir du menu
	 * impression. On ouvre un pdf writer sur un fichier dédié à l'utilisateur.
	 */
	@Override
	public String createPdfRail(User user, int railId)
			throws ApplicationException {

		Rail rail = railManager.getById(railId);
		List<Wagon> lstWagons = hubManager.getListWagonsForRail(rail);

		Rectangle a4 = PageSize.A4;
		Rectangle a4Landscape = a4.rotate();
		Document document = new Document(a4Landscape);
		try {
			hubPdfCreator.pdfDocumentRail(
					getPdfWriter(PDF_RAIL, user, document), document,
					ManagedBeanUtils.getRealPath(pdfImgFullPath), lstWagons,
					rail);
		} catch (DocumentException e) {
			throw new ApplicationException(e);
		}
		return pdfHtmlPath + PATH_HTML_SEPARATOR + user.getId()
				+ PATH_HTML_SEPARATOR + PDF_RAIL;
	}

	/**
	 * Création d'un flux pdf pour le programme de travail à partir du menu
	 * impression. On vérifie que le sillon a bien un programme de travail qui a
	 * été demandé. Si tout est bon, on rejoint le chemin normal en appelant la
	 * méthode qui est utilisée lors de la création du programme de travail.
	 */
	@Override
	public String createPdfWorkingPlan(User user, String sillonExtId,
			Date dateSillon, String tcmsTrainId) throws ApplicationException {

		Sillon sillon;
		List<Composition> lstComposition;
		StringBuffer sbStatusToAvoid = new StringBuffer("|");
		sbStatusToAvoid.append(HubSillonStatus.CREATED.getValue()).append("|");
		sbStatusToAvoid.append(HubSillonStatus.ATTRIBUTED.getValue()).append(
				"|");
		sbStatusToAvoid
				.append(HubSillonStatus.WORKING_PLAN_EXCLUDED.getValue())
				.append("|");
		sbStatusToAvoid.append(HubSillonStatus.CANCELED.getValue()).append("|");
		sbStatusToAvoid.append(
				HubSillonStatus.COMPO_PLAN_ARRIVED_NO_TRAIN.getValue()).append(
				"|");
		sbStatusToAvoid.append(
				HubSillonStatus.COMPO_PLAN_ARRIVED_TRAIN.getValue())
				.append("|");

		if (tcmsTrainId.trim().length() > 0) {
			sillon = hubManager.getSillonByTcmsTrainId(tcmsTrainId);
		} else {
			sillon = hubManager
					.getSillonByExtIdAndDate(sillonExtId, dateSillon);
		}
		// On vérifie le type de sillon.
		if (!sillon.getType().equals(HubSillonType.ARRIVAL.getValue()))
			throw new SillonTypeException(
					"Hub : Ce type de sillon ne peut avoir de programme de travail.");

		// On vérifie le statut.
		if (sbStatusToAvoid.toString().indexOf(
				"|" + Integer.toString(sillon.getStatusId()) + "|") != -1)
			throw new PlanNotFoundException("Hub : Le statut du sillon"
					+ " ne permet pas d'imprimer le programme de travail");

		lstComposition = hubManager.getPlan(sillon);
		return createPdfWorkingPlan(user, lstComposition, sillon);
	}

	/**
	 * Création d'un flux pdf pour le programme de travail. On ouvre un pdf
	 * writer sur un fichier dédié à l'utilisateur. ici on arrive directement
	 * avec les données du hub puisque le coordinateur vient d'effectuer le
	 * programme de travail. Il n'y a donc pas besoin d'aller rechercher les
	 * données en base.
	 */
	@Override
	public String createPdfWorkingPlan(User user,
			List<Composition> lstCompositions, Sillon sillon)
			throws ApplicationException {

		Rectangle a4 = PageSize.A4;
		Rectangle a4Landscape = a4.rotate();
		Document document = new Document(a4Landscape);

		try {
			hubPdfCreator.pdfDocumentWorkingPlan(
					getPdfWriter(PDF_WORKING_PLAN, user, document), document,
					ManagedBeanUtils.getRealPath(pdfImgFullPath), sillon,
					lstCompositions);
		} catch (DocumentException e) {
			throw new ApplicationException(e);
		}
		return pdfHtmlPath + user.getId() + PATH_HTML_SEPARATOR
				+ PDF_WORKING_PLAN;
	}

	/**
	 * Création d'un flux pdf pour le planning. On ouvre un pdf writer sur un
	 * fichier dédié à l'utilisateur.
	 */
	@Override
	public String createPdfPLanning(User user, Date date, Date dateEnd,
			String time, String timeEnd, String type)
			throws ApplicationException {

		List<Sillon> lstSillons = hubManager.getListSillonsByDateAndTime(date,
				dateEnd, time, timeEnd, type);

		Rectangle a4 = PageSize.A4;
		Rectangle a4Landscape = a4.rotate();
		Document document = new Document(a4Landscape);

		try {
			hubPdfCreator.pdfDocumentPlanning(
					getPdfWriter(PDF_PLANNING, user, document), document,
					ManagedBeanUtils.getRealPath(pdfImgFullPath), lstSillons,
					date, dateEnd);
		} catch (DocumentException e) {
			throw new ApplicationException(e);
		}
		return pdfHtmlPath + user.getId() + PATH_HTML_SEPARATOR + PDF_PLANNING;
	}

	/**
	 * Création d'un flux pdf pour le programme de formation à partir du menu
	 * impression. On vérifie que le sillon a bien un programme de formation qui
	 * a été demandé. Si tout est bon, on rejoint le chemin normal en appelant
	 * la méthode qui est utilisée lors de la création du programme de
	 * formation.
	 */
	@Override
	public String createPdfFormationPlan(User user, String sillonExtId,
			Date dateSillon, String tcmsTrainId) throws ApplicationException {

		Sillon sillon;
		List<Composition> lstComposition;
		StringBuffer sbStatusToAvoid = new StringBuffer("|");
		sbStatusToAvoid.append(HubSillonStatus.CREATED.getValue()).append("|");
		sbStatusToAvoid.append(HubSillonStatus.ATTRIBUTED.getValue()).append(
				"|");
		sbStatusToAvoid.append(HubSillonStatus.CANCELED.getValue()).append("|");

		if (tcmsTrainId.trim().length() > 0) {
			sillon = hubManager.getSillonByTcmsTrainId(tcmsTrainId);
		} else {
			sillon = hubManager
					.getSillonByExtIdAndDate(sillonExtId, dateSillon);
		}

		// On vérifie le type de sillon.
		if (!sillon.getType().equals(HubSillonType.DEPARTURE.getValue()))
			throw new SillonTypeException(
					"Hub : Ce type de sillon ne peut avoir de programme de formation.");

		if (sbStatusToAvoid.toString().indexOf(
				"|" + Integer.toString(sillon.getStatusId()) + "|") != -1)
			throw new PlanNotFoundException("Hub : Le statut du sillon"
					+ " ne permet pas d'imprimer le programme de formation");

		lstComposition = hubManager.getPlan(sillon);
		return createPdfFormationPlan(user, lstComposition, sillon);
	}

	/**
	 * Création d'un flux pour le programme de formation. On ouvre un pdf writer
	 * sur un fichier dédié à l'utilisateur.
	 */
	@Override
	public String createPdfFormationPlan(User user,
			List<Composition> lstCompositions, Sillon sillon)
			throws ApplicationException {

		Rectangle a4 = PageSize.A4;
		Rectangle a4Landscape = a4.rotate();
		Document document = new Document(a4Landscape);

		try {
			hubPdfCreator.pdfDocumentFormationPlan(
					getPdfWriter(PDF_FORMATION_PLAN, user, document), document,
					ManagedBeanUtils.getRealPath(pdfImgFullPath), sillon,
					lstCompositions);
		} catch (DocumentException e) {
			throw new ApplicationException(e);
		}
		return pdfHtmlPath + user.getId() + PATH_HTML_SEPARATOR
				+ PDF_FORMATION_PLAN;
	}

	/**
	 * Création d'un flux pdf pour le relevé de composition à partir du menu
	 * impression. On vérifie que le sillon a bien un relevé de composition qui
	 * a été demandé. Si tout est bon, on rejoint le chemin normal en appelant
	 * la méthode qui est utilisée lors de la création du relevé de composition.
	 */
	@Override
	public String createPdfCompositionPlan(User user, String sillonExtId,
			Date dateSillon, String tcmsTrainId) throws ApplicationException {

		Sillon sillon;
		List<Composition> lstCompositions;

		if (tcmsTrainId.trim().length() > 0) {
			sillon = hubManager.getSillonByTcmsTrainId(tcmsTrainId);
		} else {
			sillon = hubManager
					.getSillonByExtIdAndDate(sillonExtId, dateSillon);
		}

		// On vérifie le type de sillon.
		if (!sillon.getType().equals(HubSillonType.DEPARTURE.getValue()))
			throw new SillonTypeException(
					"Hub : Ce type de sillon ne peut avoir de relevé de composition.");

		// On vérifie que le sillon à le bon statut.
		if (!(sillon.getStatusId() == HubSillonStatus.DEPARTURE_READY
				.getValue() || sillon.getStatusId() == HubSillonStatus.DEPARTURE_COMPLETED
				.getValue()))
			throw new PlanNotFoundException("Hub : Le statut du sillon"
					+ " ne permet pas d'imprimer le relevé de composition");

		lstCompositions = hubManager.getPlan(sillon);

		return createPdfCompositionPlan(user, lstCompositions, sillon);
	}

	/**
	 * Création d'un flux pour le relevé de composition. On ouvre un pdf writer
	 * sur un fichier dédié à l'utilisateur.
	 */
	@Override
	public String createPdfCompositionPlan(User user,
			List<Composition> lstCompositions, Sillon sillon)
			throws ApplicationException {

		Rectangle a4 = PageSize.A4;
		Rectangle a4Landscape = a4.rotate();
		Document document = new Document(a4Landscape);

		try {
			hubPdfCreator.pdfDocumentCompositionPlan(
					getPdfWriter(PDF_COMPOSITION_PLAN, user, document),
					document, ManagedBeanUtils.getRealPath(pdfImgFullPath),
					sillon, lstCompositions);

		} catch (DocumentException e) {
			throw new ApplicationException(e);
		}
		return pdfHtmlPath + user.getId() + PATH_HTML_SEPARATOR
				+ PDF_COMPOSITION_PLAN;
	}
}
